package com.ncmem.controller;

import com.google.gson.Gson;
import com.ncmem.down3.biz.DnFile;
import com.ncmem.model.DnFileInf;
import com.ncmem.up7.database.DBConfig;
import com.ncmem.up7.database.DBFolder;
import com.ncmem.utils.PathTool;
import com.ncmem.up7.biz.folder.fd_page;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.sql.SQLException;
import java.text.ParseException;

/**
 * * Created by jmzy on 2021/3/8.
 * 接口：
 * down3/clear
 * down3/f_create
 * down3/f_post
 * down3/f_process
 * down3/f_complete
 * down3/f_del
 * down3/f_list
 * down3/fd_create
 * down3/fd_complete
 * down3/fd_del
 * 注意：
 * 2.f_post由控件自动调用。
 */
@RestController
public class Down3Api {
    @Autowired
    private HttpServletRequest req;

    @Autowired
    private HttpServletResponse res;

    /**
     * 清空数据库，由前端调用
     * @return
     */
    @RequestMapping(value="down3/clear",method = RequestMethod.GET)
    public String clear()
    {
        DBConfig cfg = new DBConfig();
        DnFile f = cfg.down();
        f.Clear();
        return "数据库清理完毕";
    }

    /**
     * 文件下载完毕，删除记录。前端调用
     * @param id
     * @param uid
     * @param cbk
     * @return
     */
    @RequestMapping(value="down3/f_complete",method = RequestMethod.GET)
    public String complete(@RequestParam(value="id", required=false,defaultValue="")String id,
                           @RequestParam(value="uid", required=false,defaultValue="0")String uid,
                           @RequestParam(value="callback", required=false,defaultValue="")String cbk) throws SQLException, ParseException {
        if (StringUtils.isBlank(id))
        {
            return cbk + "(0)";
        }

        DBConfig cfg = new DBConfig();
        DnFile f = cfg.down();
        f.Delete(id,uid);
        return  cbk + "(1)";
    }

    /**
     * 初始化下载任务，由前端调用
     * @param id
     * @param uid
     * @param f_id
     * @param nameLoc
     * @param pathLoc
     * @param fileUrl
     * @param lenSvr
     * @param sizeSvr
     * @param cbk
     * @return
     */
    @RequestMapping(value="down3/f_create",method = RequestMethod.GET)
    public String create(@RequestParam(value="id", required=false,defaultValue="")String id,
                           @RequestParam(value="uid", required=false,defaultValue="0")String uid,
                         @RequestParam(value="f_id", required=false,defaultValue="")String f_id,
                         @RequestParam(value="nameLoc", required=false,defaultValue="")String nameLoc,
                         @RequestParam(value="pathLoc", required=false,defaultValue="")String pathLoc,
                         @RequestParam(value="fileUrl", required=false,defaultValue="")String fileUrl,
                         @RequestParam(value="lenSvr", required=false,defaultValue="0")Long lenSvr,
                         @RequestParam(value="sizeSvr", required=false,defaultValue="")String sizeSvr,
                           @RequestParam(value="callback", required=false,defaultValue="")String cbk) throws ParseException, IllegalAccessException, SQLException {
        if (
                StringUtils.isBlank(pathLoc)||
                StringUtils.isBlank(fileUrl)||
                lenSvr == 0)
        {
            return cbk + "({\"value\":null}) ";
        }

        DnFileInf inf = new DnFileInf();
        inf.id		  = id;
        inf.f_id      = f_id;
        inf.uid       = uid;
        inf.nameLoc   = nameLoc;
        inf.pathLoc   = pathLoc;
        inf.fileUrl   = fileUrl;
        inf.lenSvr    = lenSvr;
        inf.sizeSvr   = sizeSvr;

        DBConfig cfg = new DBConfig();
        DnFile df = cfg.down();
        df.Add(inf);

        Gson gson = new Gson();
        String json = gson.toJson(inf);
        json = PathTool.url_encode(json);
        json = cbk + "({\"value\":\"" + json + "\"})";//返回jsonp格式数据。
        return json;
    }

    /**
     * 删除下载任务，前端调用。
     * @param id
     * @param uid
     * @param cbk
     * @return
     */
    @RequestMapping(value="down3/f_del",method = RequestMethod.GET)
    public String del(@RequestParam(value="id", required=false,defaultValue="")String id,
                         @RequestParam(value="uid", required=false,defaultValue="0")String uid,
                         @RequestParam(value="callback", required=false,defaultValue="")String cbk) throws SQLException, ParseException {

        if ( StringUtils.isBlank(id) )
        {
            return cbk + "({\"value\":null})";
        }

        DBConfig cfg = new DBConfig();
        DnFile f = cfg.down();
        f.Delete(id,uid);

        return cbk+"({\"value\":1})";
    }

    /**
     * 下载文件块
     * @param pathSvr 文件路径
     * @param blockPath 块路径
     * @param blockIndex  块索引，基于1
     * @param blockOffset 块偏移位置，基于0
     * @param blockSize   当前块大小
     */
    @RequestMapping(value="down3/f_down_part",method = RequestMethod.GET)
    public void down_part(@RequestParam(value="pathSvr", required=false,defaultValue="")String pathSvr,
                      @RequestParam(value="blockPath", required=false,defaultValue="")String blockPath,
                      @RequestParam(value="blockIndex", required=false,defaultValue="0")Integer blockIndex,
                      @RequestParam(value="blockOffset", required=false,defaultValue="0")Long blockOffset,
                      @RequestParam(value="blockSize", required=false,defaultValue="0")Integer blockSize
                      )
    {
        if ( StringUtils.isEmpty(blockPath) ||
                blockSize == 0
        )
        {
            res.setStatus(500);
            res.setHeader("err","参数为空");
            String msg = String.format("blockIndex:%s,blockOffset:%s,blockSize:%s,pathSvr:%s",
                    blockIndex,
                    blockOffset,
                    blockSize,
                    pathSvr);
            try {
                res.getOutputStream().write(msg.getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }
        blockPath = PathTool.url_decode(blockPath);

        File f = new File(pathSvr);
        //文件不存在
        if(!f.exists())
        {
            res.setStatus(500);
            OutputStream os = null;
            try {
                os = res.getOutputStream();
                String msg = String.format("%s 文件不存在",pathSvr);
                os.write(msg.getBytes());
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }

        //文件存在
        long fileLen = f.length();
        res.setContentType("application/x-download");
        res.setHeader("Pragma","No-cache");
        res.setHeader("Cache-Control","no-cache");
        res.addHeader("Content-Length",blockSize.toString());
        res.setDateHeader("Expires", 0);

        OutputStream os = null;
        try {
            os = res.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try
        {
            RandomAccessFile raf = new RandomAccessFile(pathSvr,"r");

            Integer readToLen = blockSize;
            int readLen = 0;
            raf.seek( blockOffset );//定位索引
            byte[] data = new byte[1048576];//1MB

            while( readToLen > 0 )
            {
                readLen = raf.read(data,0,Math.min(1048576,readToLen) );
                readToLen -= readLen;
                os.write(data, 0, readLen);

            }
            os.flush();
            os.close();
            raf.close();
            os = null;
            res.flushBuffer();
        }
        catch(Exception e)
        {
            res.setStatus(500);
            try {
                os.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
        finally
        {
            if(os != null)
            {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                os = null;
            }
        }
    }

    /**
     * 下载文件块
     * @param pathSvr 远程文件路径
     * @param blockIndex 块索引，基于1
     * @param fileOffset 块偏移，相对于整个文件。
     * @param blockSize  当前块未下载的大小。
     */
    @RequestMapping(value="down3/f_down",method = RequestMethod.GET)
    public void f_down(@RequestHeader(value="pathSvr", required=false,defaultValue="")String pathSvr,
                          @RequestHeader(value="blockIndex", required=false,defaultValue="0")Integer blockIndex,
                          @RequestHeader(value="fileOffset", required=false,defaultValue="0")Long fileOffset,
                          @RequestHeader(value="blockSize", required=false,defaultValue="0")Integer blockSize
    )
    {
        if ( StringUtils.isEmpty(pathSvr) )
        {
            res.setStatus(500);
            res.setHeader("err","参数为空");
            String msg = String.format("blockIndex:%s,fileOffset:%s,blockSize:%s,pathSvr:%s",
                    blockIndex,
                    fileOffset,
                    blockSize,
                    pathSvr);
            try {
                res.getOutputStream().write(msg.getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }
        pathSvr = PathTool.url_decode(pathSvr);

        //文件不存在
        File f = new File(pathSvr);
        if(!f.exists())
        {
            res.setStatus(500);
            OutputStream os = null;
            try {
                os = res.getOutputStream();
                String msg =String.format("%s 文件不存在",pathSvr);
                os.write(msg.getBytes());
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return;
        }

        //文件存在
        long fileLen = f.length();
        res.setContentType("application/x-download");
        res.setHeader("Pragma","No-cache");
        res.setHeader("Cache-Control","no-cache");
        res.addHeader("Content-Length",blockSize.toString());
        res.setDateHeader("Expires", 0);

        OutputStream os = null;
        try {
            os = res.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try
        {
            RandomAccessFile raf = new RandomAccessFile(pathSvr,"r");

            Integer readToLen = blockSize;//需要下载的块大小
            int readLen = 0;
            raf.seek( fileOffset );//定位索引
            byte[] data = new byte[1048576];//1MB

            while( readToLen > 0 )
            {
                readLen = raf.read(data,0,Math.min(1048576,readToLen) );
                readToLen -= readLen;
                os.write(data, 0, readLen);

            }
            os.flush();
            os.close();
            raf.close();
            os = null;
            res.flushBuffer();
        }
        catch(Exception e)
        {
            res.setStatus(500);
            try {
                os.close();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
        finally
        {
            if(os != null)
            {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                os = null;
            }
        }
    }

    /**
     * 列出已经上传完的文件列表，前端调用
     * @param uid 用户ID
     * @param cbk jquery回调方法，由ajax提供
     * @return
     */
    @RequestMapping(value="down3/f_list_cmp",method = RequestMethod.GET)
    public String f_list_cmp(@RequestParam(value="uid", required=false,defaultValue="")String uid,
                           @RequestParam(value="callback", required=false,defaultValue="")String cbk
    ) throws SQLException, InstantiationException, ParseException, IllegalAccessException {
            String json = DnFile.build().all_complete(uid );
            if(!StringUtils.isBlank(json))
            {
                System.out.println("上传文件列表："+json);
                json = PathTool.url_encode(json);
                return cbk + "({\"value\":\""+json+"\"})";
            }
        return cbk+"({\"value\":null})";
    }

    /**
     * 列出未下载完的文件和目录列表，由前端调用。
     * @param uid 用户ID，
     * @param cbk jquery回调方法，
     * @return
     */
    @RequestMapping(value="down3/f_list",method = RequestMethod.GET)
    public String f_list(@RequestParam(value="uid", required=false,defaultValue="")String uid,
                             @RequestParam(value="callback", required=false,defaultValue="")String cbk
    ) throws SQLException, InstantiationException, ParseException, IllegalAccessException {
        String json = DnFile.build().all_uncmp( uid);

        if(!StringUtils.isBlank(json))
        {
            json = PathTool.url_encode(json);
            json = cbk + "({\"value\":\""+json+"\"})";
            return json;
        }

        return cbk + "({\"value\":null})";
    }

    /**
     * 更新文件下载进度，由前端调用。
     * @param id      下载任务ID
     * @param uid     用户ID
     * @param sizeLoc 已下载的文件大小，
     * @param perLoc  已下载的百分比
     * @param cbk     jquery回调方法
     * @return
     */
    @RequestMapping(value="down3/f_update",method = RequestMethod.GET)
    public String f_update(@RequestParam(value="id", required=false,defaultValue="")String id,
                           @RequestParam(value="uid", required=false,defaultValue="0")String uid,
                           @RequestParam(value="sizeLoc", required=false,defaultValue="")String sizeLoc,
                           @RequestParam(value="perLoc", required=false,defaultValue="")String perLoc,
                         @RequestParam(value="callback", required=false,defaultValue="")String cbk
    ) throws ParseException, IllegalAccessException, SQLException {
        if (
                StringUtils.isBlank(id)||
                StringUtils.isBlank(cbk))
        {
            return cbk + "({\"value\":0})";
        }

        DnFile db = new DnFile();
        db.process(id,uid,sizeLoc,perLoc);

        return cbk + "({\"value\":1})";
    }

    /**
     * 创建文件夹下载任务，前端调用。
     * @param id      文件夹ID
     * @param uid     用户ID
     * @param nameLoc 本地目录名称
     * @param sizeSvr 文件夹大小（远程）
     * @param pathLoc 本地路径
     * @param cbk     jquery回调方法名称，
     * @return
     */
    @RequestMapping(value="down3/fd_create",method = RequestMethod.GET)
    public String fd_create(@RequestParam(value="id", required=false,defaultValue="")String id,
                           @RequestParam(value="uid", required=false,defaultValue="0")String uid,
                            @RequestParam(value="nameLoc", required=false,defaultValue="")String nameLoc,
                           @RequestParam(value="sizeSvr", required=false,defaultValue="")String sizeSvr,
                           @RequestParam(value="pathLoc", required=false,defaultValue="")String pathLoc,
                           @RequestParam(value="callback", required=false,defaultValue="")String cbk
    ) throws ParseException, IllegalAccessException, SQLException {
        nameLoc = PathTool.url_decode(nameLoc);
        pathLoc = PathTool.url_decode(pathLoc);
        sizeSvr = PathTool.url_decode(sizeSvr);

        if (!StringUtils.isBlank(nameLoc)||
                !StringUtils.isBlank(pathLoc)||
                !StringUtils.isBlank(sizeSvr)
        )
        {
            DnFileInf fd = new DnFileInf();
            fd.nameLoc = nameLoc;
            fd.pathLoc = pathLoc;
            fd.id= id;
            fd.uid = uid;
            fd.fdTask = true;
            fd.sizeSvr = sizeSvr;

            DnFile db = new DnFile();
            db.Add(fd);

            Gson gson = new Gson();
            String json = gson.toJson(fd);
            json = PathTool.url_encode(json);
            json = cbk + "({\"value\":\""+json+"\"})";

            return json;
        }
        return "";
    }

    /**
     * 获取文件夹分页数据或文件夹子文件总数，由控件调用
     * @param id 文件夹ID
     * @param op 操作方法，count,page
     * @param page 页索引，基于1
     * @return
     */
    @RequestMapping(value="down3/fd_page",method = RequestMethod.GET)
    public String fd_page(@RequestParam(value="id", required=false,defaultValue="")String id,
                            @RequestParam(value="op", required=false,defaultValue="")String op,
                          @RequestParam(value="page", required=false,defaultValue="1")String page
    ) throws ParseException, IllegalAccessException, SQLException {
        if( StringUtils.equals(op, "count"))
        {
            int count = DBFolder.count(id);
            JSONObject obj = new JSONObject();
            obj.put("count",count);
            return obj.toString();
        }
        else
        {
            if (StringUtils.isBlank(id)||
                    StringUtils.isBlank(page)
            )
            {
                String msg = "fd_page.jsp 分页数据获取失败";
                try {
                    res.getOutputStream().write(msg.getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return "";
            }

            fd_page fd = new fd_page();
            String json = fd.read(page,id);
            json = PathTool.url_encode(json);
            //res.setHeader("Content-Length",json.length()+"");//返回Content-length标记，以便控件正确读取返回地址。
            return json;
        }
    }
}
